package com.hzmg.web.creater;

import com.hzmg.web.common.ResultDTO;
import com.hzmg.web.common.WebConstant;
import com.hzmg.web.creater.handler.InitSingleRouterHandler;
import com.hzmg.web.utils.SpringUtils;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * vert.x web API creater
 *
 * @author zbw
 */
@Slf4j
public class WebApiCreater extends AbstractVerticle {

    private Router router;

    private ApplicationContext springContext;

    private InitSingleRouterHandler initSingleRouterHandler = (InitSingleRouterHandler) SpringUtils.getBean(InitSingleRouterHandler.class);

    public WebApiCreater(ApplicationContext applicationContext) {
        springContext = applicationContext;
    }

    private static void out(RoutingContext ctx, Object msg) {
        ctx.response()
                .putHeader(WebConstant.HTTP_HEADER_CONTENT_TYPE, WebConstant.JSON_MEDIA_TYPE)
                .end(JsonObject.mapFrom(msg).encode());
    }

    private static void success(RoutingContext ctx) {
        out(ctx, ResultDTO.success(null));
    }

    @Override
    public void start() {
        log.info("[WebAPiCreater] start...");
        Integer port = springContext.getEnvironment().getProperty("server.port", Integer.class);
        HttpServerOptions options = new HttpServerOptions();
        HttpServer server = vertx.createHttpServer(options);

        router = Router.router(vertx);
        router.route().handler(BodyHandler.create());

        cros(router);
        //将所有的controller转换成router
        try {
            // 获取所有beanNames
            String[] beanNames = SpringUtils.getBeanNamesForType(Object.class);
            for (String beanName : beanNames) {
                RestController restController = SpringUtils.findAnnotationOnBean(beanName,
                        RestController.class);
                //判断该类是否含有RestController注解
                if (restController != null) {
                    RequestMapping requestMapping = SpringUtils.findAnnotationOnBean(beanName,
                            RequestMapping.class);
                    String prefixName;
                    if (requestMapping == null) {
                        prefixName = "";
                    } else {
                        prefixName = "".equals(requestMapping.name()) ? requestMapping.value()[0] : requestMapping.name();
                    }
                    log.info("[vertx web] prefix:" + prefixName);
                    Object newInstance = SpringUtils.getBean(beanName);
                    Method[] methods = ReflectionUtils.getAllDeclaredMethods(newInstance.getClass());
                    for (Method method : methods) {
                        initSingleRouterHandler.exec(method,prefixName,newInstance,router);
                    }
                }
            }
            log.info("[Vertx web] Create web for Spring-web with vertx web succeed!");
        } catch (Exception e) {
            throw new RuntimeException(e.toString());
        }
        server.requestHandler(router).listen(port);
    }
    private void cros(Router router) {
        Set<String> allowedHeaders = new HashSet<>();
        allowedHeaders.add("x-requested-with");
        allowedHeaders.add("Access-Control-Allow-Origin");
        allowedHeaders.add("origin");
        allowedHeaders.add("Content-Type");
        allowedHeaders.add("accept");
        allowedHeaders.add("X-PINGARUNER");

        Set<HttpMethod> allowedMethods = new HashSet<>();
        allowedMethods.add(HttpMethod.GET);
        allowedMethods.add(HttpMethod.POST);
        allowedMethods.add(HttpMethod.OPTIONS);
        /*
         * these methods aren't necessary for this sample,
         * but you may need them for your projects
         */
        allowedMethods.add(HttpMethod.DELETE);
        allowedMethods.add(HttpMethod.PATCH);
        allowedMethods.add(HttpMethod.PUT);

        router.route().handler(CorsHandler.create("*").allowedHeaders(allowedHeaders).allowedMethods(allowedMethods));
    }
}
